package com.miyake.server;

import java.io.BufferedInputStream;
import java.util.ArrayList;
import java.util.List;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.miyake.client.datamanage.PlotData;
import com.miyake.server.dsaccess.NodeManager;
import com.miyake.server.dsaccess.PluginManager;
import com.miyake.server.servlet.ConvertStreamToByte;
import com.miyake.server.servlet.FileServlet;

@PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class DsPlot {
	@PrimaryKey 
	@Persistent(valueStrategy=IdGeneratorStrategy.IDENTITY)  
	private Key key;
	
	@Persistent
	private int type;
	
	@Persistent
	private String nodeKey;
	
	@Persistent
	private Double xmin;
	
	@Persistent
	private Double xmax;
	
	@Persistent
	private Double ymin;
	
	@Persistent
	private Double ymax;
	
	@Persistent
	private Integer xColumn;
	
	@Persistent
	private Integer yColumn;
	
	@Persistent
	private String chartPlugin;
	
	@Persistent
	private List<String> overlays = new ArrayList<String>();
	
	public String getEncodedKey() {
		return KeyFactory.keyToString(key);
	}

	public void setKey(Key key) {
		this.key = key;
	}


	public int getType() {
		return type;
	}


	public void setType(int type) {
		this.type = type;
	}


	public String getNodeKey() {
		return nodeKey;
	}


	public void setNodeKey(String nodeKey) {
		this.nodeKey = nodeKey;
	}

	public Double getXmin() {
		return xmin;
	}


	public void setXmin(Double xmin) {
		this.xmin = xmin;
	}


	public Double getXmax() {
		return xmax;
	}


	public void setXmax(Double xmax) {
		this.xmax = xmax;
	}


	public Double getYmin() {
		return ymin;
	}


	public void setYmin(Double ymin) {
		this.ymin = ymin;
	}


	public Double getYmax() {
		return ymax;
	}


	public void setYmax(Double ymax) {
		this.ymax = ymax;
	}

	public Integer getxColumn() {
		return xColumn;
	}


	public void setxColumn(Integer xColumn) {
		this.xColumn = xColumn;
	}


	public Integer getyColumn() {
		return yColumn;
	}


	public void setyColumn(Integer yColumn) {
		this.yColumn = yColumn;
	}


	public List<String> getOverlays() {
		return overlays;
	}

	public void setOverlays(List<String> overlays) {
		this.overlays = overlays;
	}

	private Double[] getPlotY(DsData data, Integer count) {
		Double[] y = null;

		y = data.getDataDouble(yColumn);

		
//		double step = (double)y.length / (double)count;

		int startIndex =(int)( (xmin * y.length) / data.getXmax() );
		int stopIndex = (int)( (xmax * y.length) / data.getXmax() );
		
		if (stopIndex - startIndex > count) {
			double step = y.length * (xmax - xmin)/(data.getXmax() - data.getXmin()) / count;
			Double[] ploty = new Double[count];
			int index = 0;
			for (int i = 0; i < ploty.length; i++) {
				index = startIndex + (int)((double)i * step);
				if (index < 0)index = 0;
				if (index > y.length - 1) {
					index = y.length - 1;
				}
				ploty[i] = y[index];
			}
			return ploty;
		}
		else {
			Double[] ploty = new Double[stopIndex - startIndex];
			for (int i = 0; i < ploty.length; i++) {
				ploty[i] = y[startIndex + i];
			}
			return ploty;
		}
		
	}
	private String dataSequence(Double[] ploty) {	
		String ret = "";
		boolean first = true;
		for (Double d : ploty) {
			if (first) {
				first = false;
			}
			else {
				ret += ",";
			}
			ret += String.format("%.2f", d.doubleValue());
			
		}
		return ret;
	}

	public String getChartURL(int count, int height) {
		String key = NodeManager.getInstance().getNode(nodeKey).getParentKey();
		return getChartURL(getPlotY(NodeManager.getInstance().getData(key), count), height);
	}
	
	private String getChartURL(Double[] ploty, int height) {
		final String url;
		if (this.chartPlugin == null) { // Default Chart
			url = "https://chart.googleapis.com/chart";
		}
		else {
			url = PluginManager.getInstance().getPlugin(this.chartPlugin).getUrl();
		}
		
		List<String> args = new ArrayList<String>();
		//final String url = "https://chart.googleapis.com/chart";
		int width = ploty.length;
		args.add("cht=lc"); // type
		args.add("chs="+ String.valueOf(width) + "x" + String.valueOf(height)); // size
		args.add("chg=20,10"); // Grid
		args.add("chls=1"); // line width
		args.add("chco=FF0000"); // line color
		args.add("chds=" + String.format("%.2f", ymin) + "," + String.format("%.2f", ymax)); // Min Max
		args.add("chxt=x,y");
		args.add("chl=" + xmin + "|" + xmax); // label
		
		String data = "chd=t:" + dataSequence(ploty);
		args.add(data);

		String body = "";
		for (String s : args) {
			body += s;
			body += "&";
		}
		body += "dummy=" + String.valueOf(Math.random()).substring(0, 8);
		// Gets image by HTTP GET
		if (body.length() < 2048) {
			return url + "?" + body;
		}
		else { // Gets image by HTTP POST
			final String body2 = body;
			new HttpPost() {
				@Override
				public String getUrl() {
					return url;
				}
	
				@Override
				public String getPostData() {
					return body2;
				}
	
				@Override
				public String getContentType() {
					return "application/x-www-form-urlencoded";
				}
	
				@Override
				public void setInputStream(BufferedInputStream is) {
					byte[] b = new ConvertStreamToByte().convert(is);
					FileServlet.getChartMap().put(getEncodedKey(), b);
				}
			};
			// dummy parameter to avoid data caching by web browser.
			String dummyParameter = "&dummy=" + String.valueOf(Math.random()).substring(0, 8);
			return "/fileServlet?type=chart&key=" + getEncodedKey() + dummyParameter;
		}
		
	}

	public PlotData toRemoteObject(Integer count) {
		String key = NodeManager.getInstance().getNode(nodeKey).getParentKey();
		DsData data = NodeManager.getInstance().getData(key);
		PlotData plot = new PlotData();
		Double[] ploty = getPlotY(data, count);
		plot.setY(ploty);
		plot.setPlotType(this.type);
		plot.setDataType(data.getType());
//		plot.setUrl(getChartURL(ploty, 200));
		plot.setKey(getEncodedKey());
		plot.setNodeKey(nodeKey);
		plot.setXmax(xmax);
		plot.setXmin(xmin);
		plot.setYmin(ymin);
		plot.setYmax(ymax);
		for (String o : this.overlays) {
			plot.getOverlays().add(o);
		}
		return plot;
	}

	public void setDefaultScale() {
		String key = NodeManager.getInstance().getNode(nodeKey).getParentKey();
		DsData data = NodeManager.getInstance().getData(key);
		
		this.xmin = new Double(data.getXmin());
		this.xmax = new Double(data.getXmax());
		
		double min = Double.MAX_VALUE;
		double max = Double.MIN_VALUE;
		Double[] d = data.getDataDouble(yColumn);
		for (int i = 0; i < d.length; i++) {
			if (d[i] < min) {
				min = d[i];
			}
			if (d[i] > max) {
				max = d[i];
			}
		}
		this.ymin = min * 0.95;
		this.ymax = max * 1.05;
	}
}
